﻿using System;
using System.Collections.Generic;
using System.Linq;
using Validity.Rules;

namespace Validity.Contexts
{
  public interface IValidationContext<TObject, TValue>
  {
    IConditionalValidationContext<TObject, TValue> AddRule(IRule<TObject, TValue> rule);

    void DecorateLastRuleWith(Func<IRule<TObject, TValue>, IRule<TObject, TValue>> decorator);
  }

  public class ValidationContext<TObject, TValue>
    : IValidationContext<TObject, TValue>
  {
    private readonly IList<IRule<TObject, TValue>> _rules;

    protected virtual IList<IRule<TObject, TValue>> Rules { get { return _rules; } }

    public ValidationContext()
    {
      _rules = new List<IRule<TObject, TValue>>();
    }

    public ValidationContext(IValidationContext<TObject, TValue> context)
      : this((ValidationContext<TObject, TValue>)context)
    { }

    private ValidationContext(ValidationContext<TObject, TValue> context)
    {
      Verify.NotNull(context);

      _rules = context.Rules;
    }
    
    public IConditionalValidationContext<TObject, TValue> AddRule(IRule<TObject, TValue> rule)
    {
      Verify.NotNull(rule);

      Rules.Add(rule);

      return new ConditionalValidationContext<TObject, TValue>(this);
    }

    public void DecorateLastRuleWith(Func<IRule<TObject, TValue>, IRule<TObject, TValue>> decorator)
    {
      Verify.NotNull(decorator);

      if (Rules.Count == 0)
        return;

      var index = Rules.Count - 1;

      Rules[index] = decorator.Invoke(Rules[index]);
    }

    public IEnumerable<RuleViolation> GetRuleViolationsFor(TObject obj, TValue value)
    {
      return Rules.Where(rule => rule.IsViolatedBy(obj, value)).Select(rule => rule.GetRuleViolation(obj, value));
    }
  }
}